(图论)最大流算法 |
您所在的位置:网站首页 › 图论 例题 › (图论)最大流算法 |
最大流算法
网络流基础概念
网络流
在一个有向图G=(V,E)G=(V,E)中: 有一个唯一的源点S(入度为00:出发点) 有一个唯一的汇点T(出度为00:结束点) 图中的每一条边都一个非负的权值,这个权值叫做容量c(u,v)c(u,v) 满足上述条件的图GG称为网络流图,记为G=(V,E,C)G=(V,E,C) 可以想象成,如果把每条边都看成一个管道,可以看成是水从源点S出发经过这些管道,最终流向汇点T,而每条管道有最大的容量。(地下排水管道) 流量:每条弧上给定一个实数f(u,v)f(u,v),满足0≤f(u,v)≤c(u,v)0≤f(u,v)≤c(u,v) 可行流满足: 源点S:流出量 = 整个网络的流量 汇点T:流入量 = 整个网络的流量 中间的点:总流入量 = 总流出量,同时0≤f(u,v)≤c(u,v)0≤f(u,v)≤c(u,v) 这样的在整个网络中的流量就是可行流,可行流有多个 所有可行流中流量最大的流量 最大流可能不止一个 建立反向边(寻找增广路径增加最大流): 首先要明白一点,在一条从S到T的路径中,能够带来的最大流量取决于这条路径上的最小容量。 假设我们找到的路径是1−>2−>3−>4,现在的流量是1,因为这条路已经使用过了,把这条路径上的每条边减1,再次找从1到4的路径,发现找不到了,因此得到的答案为1,但是正确的答案应该是2。即1−>2−>4、1−>3−>4 这个时候,为了能够继续找到路径,必须要反向建立边,把当前路径反向建边,边的权值就是这条路径的流量 为什么要反向建边呢,仔细想想应该能够明白,反向建立边的作用相当于让之前的路径有可以反悔的余地。这样即使一开始走错也没有关系,因为可以通过反向边来反悔,最终一定能得到正确答案 增广路径明白了反向建边后,来看什么是增广路经? 如果一个可行流不是最大流,那么当前网络中一定存在一条增广路经。 从源点S到汇点T的一条路径中,如果边(u,v)与该路径的方向一致就称为正向边,否则就记为逆向边,如果在这条路径上的所有边满足 正向边f(u,v)0 则该路径是增广路径 其实增广路径就是通过这样一条路径,来增加到达汇点的流量,而路径中的流量没到达容量。 (在上图中,其实每次找到的一条从1到4的路径都是一条增广路径) 沿这条增广路改进可行流的操作称为增广.所有可能的增广路径放在一起就构成了残余网络 模板题:蓝桥杯算法训练 网络流裸题ALGO-247 2020-03-27 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 一个有向图,求1到N的最大流 输入格式 第一行N M,表示点数与边数 接下来M行每行s t c表示一条从s到t的容量为c的边 输出格式 一个数最大流量 样例输入 6 10 1 2 4 1 3 8 2 3 4 2 4 4 2 5 1 3 4 2 3 5 2 4 6 7 5 4 6 5 6 3 样例输出 8 数据约定: n3 -> 4 这条路 然后再1->2->4 那么最后的结果是1+1=2 但我们可以看出来这题最优的走法是1 - >2 -> 4 然后 1 -> 3 -> 4 最大流是2+1=3 所以要找一个可以后悔的方法,那就是对每条边加一条反向的边 第一次: 1->2 ->3 ->4 首先用一个map存放边,需要注意的是题中可能重复对一条路输入,就是说输入s t c的时候可能出现1 2 1 下一次输入又出现1->2这条路 ,所以写的时候不能mp[i][j]=c,而是要mp[i][j]+=c,刚开始提交的时候一直没通过,后来发现了这个问题。 然后定义一个数组 a,a[i]代表从源点到i的最大流通量,即每次源点开始搜索,最后的a[n]就是从源点到汇点的最大流通量,我们还要定义一个数组pre,pre[i]的意义是i的前一个点。 在每一次搜索完成之后,如果a[n]==0,说明已经没有剩余可以流通的量了,直接return;否则,存放结果的res+=本次搜索的流量a[n],res+=a[n],然后从点n开始,到1结束,更新我们这次搜索走过的路的流量,点i的上一个点就是pre[i],所以mp[pre[i]][i]+=a[n],mp[i][pre[i]]-=a[n]; #include #include #include #include using namespace std; int N,M; int s,t; int res; map mp;//描述有向图 vector a;//代表当前增广路径的最大流通量 vector pre;//记忆路径 void Solution(){ queue que; while(1){//从源点开始找 a.assign(N+1,0); //初始化 a a[s]=INT_MAX; //源点相当于有无限的流量 que.push(s); //每次都是从源点开始找 while(!que.empty()){ //bfs找增广路 int v=que.front(); que.pop(); for(map::iterator i=mp[v].begin();i!=mp[v].end();++i){//map迭代器,以v为起点寻找所有可能的下一个点 if(!a[i->first]&&i->second>0){ pre[i->first]=v;//保存上一个点,后面更新路径时使用pre数组更新数据 a[i->first]=min(a[v],i->second);//容量最小为多少,那么这条路的流量最大就是多少 que.push(i->first); } } } if(a[t]==0)return;//没有剩余的流量了 res+=a[t]; for(int i=N;i!=1;i=pre[i]){//更新走过的路的流量 mp[pre[i]][i]-=a[t];//正向容量减去流过的流量 mp[i][pre[i]]+=a[t];//反向容量加上流过的流量 } } } int main(){ cin>>N>>M; for(int i=0;i |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |